Groovy

Groovy - гибкий динамический язык для Java Virtual Machine. Начиная с версии OpenXava 4m6, вы можете использовать Groovy вместо Java для создания ваших приложений OX. Вы даже можете сочетать оба этих языка в одном и том же приложении.

Конфигурация Eclipse

Простейший способ работы с Groovy в OpenXava - использование плагина Groovy Eclipse. С инструкцией по установке плагина в Eclipse вы можете ознакомиться здесь.
После установки плагина вам необходимо будет добавить Groovy nature к уже существующим проектам в Eclipse: щелкните правой кнопкой на проекте и выберите Configure > Convert to Groovy project. Это необходимо сделать только для проектов, созданных в версиях OX ниже v4m6. Начиная с этой версии, Groovy nature добавлена по умолчанию.
Теперь вы можете добавлять классы Groovy в ваш проект, или конвертировать выбранные вами Java классы в Groovy классы. При нажатии Ctrl-B исходные тексты из .groovy файлов будут компилироваться в java файлы .class автоматически.

Использование командной строки

Если вы работаете из командной строки, не используя Eclipse, добавьте новый ant target в build.xml вашего проекта:
<!--
Compile all .java and .groovy
Not needed to call it if you work inside Eclipse
-->
<target name="compile">
<ant antfile="../OpenXava/build.xml" target="compile"/>
</target>
Не забудьте вызвать это задание перед вызовом следующего задания:
$ ant compile deployWar
Для новых проектов compile ant target is включена в build.xml по умолчанию.

Примеры кода


Вы можете создавать JPA сущности с помощью Groovy:
package org.openxava.test.model;
 
import javax.persistence.*
 
import org.openxava.annotations.*
import org.openxava.calculators.*
import org.openxava.jpa.*
 
@Entity
@Table(name="TOrder")
@View(members="""
year, number, date;
customer;
details;
amount;
remarks
"""
)
 
class Order extends Identifiable {
 
@Column(length=4)
@DefaultValueCalculator(CurrentYearCalculator.class)
int year
 
@Column(length=6)
int number
 
@Required
@DefaultValueCalculator(CurrentDateCalculator.class)
Date date
 
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@ReferenceView("Simplest")
Customer customer
 
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
@ListProperties("product.number, product.description, quantity, product.unitPrice, amount")
Collection<OrderDetail> details = new ArrayList<OrderDetail>()
 
@Stereotype("MEMO")
String remarks
 
@Stereotype("MONEY")
BigDecimal getAmount() {
BigDecimal result = 0
details.each { OrderDetail detail ->
result += detail.amount
}
return result
}
 
@PrePersist
void calculateNumber() throws Exception {
Query query = XPersistence.getManager()
.createQuery("select max(o.number) from Order o " +
"where o.year = :year")
query.setParameter("year", year)
Integer lastNumber = (Integer) query.getSingleResult()
this.number = lastNumber == null?1:lastNumber + 1
}
 
}
Также вы можете создавать Действия:

package org.openxava.test.actions
 
import org.openxava.actions.*
 
class ChangeYearConditionAction extends TabBaseAction {
 
int year;
 
void execute() throws Exception {
tab.setConditionValue("year", year)
}
 
}
Или даже тесты:
package org.openxava.test.tests
 
import javax.persistence.*
 
import org.openxava.tests.*
import org.openxava.util.*
 
import com.gargoylesoftware.htmlunit.html.*
 
import static org.openxava.jpa.XPersistence.*
 
class OrderTest extends ModuleTestBase {
 
OrderTest(String testName) {
super(testName, "Order")
}
 
void testCalculatedPropertiesFromCollection_generatedValueOnPersistRefreshedInView()
throws Exception
{
String nextNumber = getNextNumber()
execute("CRUD.new")
assertValue("number", "")
setValue("customer.number", "1")
assertValue("customer.name", "Javi")
assertCollectionRowCount("details", 0)
execute("Collection.new", "viewObject=xava_view_details")
setValue("product.number", "1")
assertValue("product.description", "MULTAS DE TRAFICO")
assertValue("product.unitPrice", "11.00")
setValue("quantity", "10")
assertValue("amount", "110.00")
execute("Collection.save")
assertNoErrors()
assertCollectionRowCount("details", 1)
assertValue("amount", "110.00")
assertValue("number", nextNumber)
execute("CRUD.delete")
assertNoErrors()
}
 
void testDoubleClickOnlyInsertsACollectionElement() throws Exception {
execute("CRUD.new")
setValue("customer.number", "1")
assertCollectionRowCount("details", 0)
execute("Collection.new", "viewObject=xava_view_details")
setValue("product.number", "1")
setValue("quantity", "10")
HtmlElement action = getForm().getElementById(decorateId("Collection.save"))
 
action.click() // Not dblClick(), it does not reproduce the problem
action.click()
Thread.sleep(3000)
 
assertNoErrors()
assertCollectionRowCount("details", 1)
 
execute("CRUD.delete")
assertNoErrors()
}
 
private String getNextNumber() throws Exception {
Query query = getManager().
createQuery(
"select max(o.number) from Order o where o.year = :year")
query.setParameter("year", Dates.getYear(new Date()))
Integer lastNumber = (Integer) query.getSingleResult()
if (lastNumber == null) lastNumber = 0
return Integer.toString(lastNumber + 1)
}
 
}

Будьте осторожными со вложенными аннотациями

Большая часть синтаксиса Java совместима с Groovy. Тем не менее, небольшой нюанс синтаксиса Groovy должен быть принят во внимание при работе со вложенными аннотациями - вам необходимо использовать [] вместо {}, как показано ниже:
@Views([ // Use [ instead of {
@View(members=""" // Use """ for multiline strings
building [
name, function;
address
]
"""),
@View(name="Simple", members="name")
]) // Use ] instead of }
Обратите внимание, что используется @Views([ ... ]) вместо @Views({ ... }). Также хочется отметить, что вы можете использовать """ для многострочных строк.
За более подробным описанием синтаксиса Groovy обратитесь к Groovy style and language feature guidelines for Java developers.